﻿using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.InteropServices;

namespace CloudShot.Utils.Tweens
{
	//NOTE http://www.republicofcode.com/tutorials/flash/as3tweenclass/
  public class Tween
	{

		#region Nested Types

		private class TweenState
		{
			public readonly float Begin;
			public readonly float End;
			public readonly float Duration;

			public readonly Func<double, EasingType, float> EasingFunction;// returns from 0 to 1 or vice versa
			public readonly EasingType EasingType;


			public TweenState( float begin, float end, float duration, Func<double, EasingType, float> easingFunction, EasingType easingType )
			{
				Begin = begin;
				End = end;
				Duration = duration;

				EasingType = easingType;
				EasingFunction = easingFunction;
			}

			public float GetValue( float time )
			{
				return Begin + ( End - Begin ) * EasingFunction( time / Duration, EasingType );
			}
		}

		#endregion

		
		private readonly Func<double, EasingType, float> _easingFunction;
		private readonly EasingType _easingType;

    private Action _callBackAction;

		private float Begin { get; set; }
		private float End { get; set; }
		public int Duration { get; set; }

	  public AnimationAdapter Adapter
	  {
	    get { return _adapter; }
	    private set
	    {
	      _adapter = value;
	      if (_adapter == null)
	        throw new NullReferenceException("adapter");

	      Begin = _adapter.Begin;
	      End = _adapter.End;
	    }
	  }

	  public bool IsStarted { get; private set; }

		private int _time;
		private TweenState _currentState = null;
	  private AnimationAdapter _adapter;

	  #region Events

#pragma warning disable 67
    //public event Action MotionFinish;	// This event is triggered when the motion finishes by reaching its end point.
    //public event Action MotionStop;		// This event is triggered when the motion is stopped by the Stop() method.
    //public event Action MotionStart;	// This event is triggered when the motion is started using the Start() method.
    //public event Action MotionResume;	// This event is triggered when the motion is resumed by the Resume() method.
    //public event Action MotionChange;	// This event is continuously triggered while the motion is running.
		//public event Action MotionLoop;		// This event is supposed to be triggered when the motion is restarted through a looping action.
#pragma warning restore 67

		#endregion

    public Tween(Func<double, EasingType, float> easingFunction, EasingType easingType, int duration, AnimationAdapter adapter, Action callBackAction = null)
    {
      Adapter = adapter;
      _callBackAction = callBackAction;

      _easingFunction = easingFunction;
			_easingType = easingType;
      
			Duration = duration;
		}

		public void Start()
		{
			_time = 0;
			_currentState = new TweenState( Begin, End, Duration, _easingFunction, _easingType );
      IsStarted = true;
			//EventHelper.Invoke( MotionStart );
		}

		public void Stop()
		{
			IsStarted = false;
			//EventHelper.Invoke( MotionStop );
		}

		public void Resume()
		{
			IsStarted = true;
			//EventHelper.Invoke( MotionResume );
		}

		private void MotionFinished()
		{
      Adapter.SetValue(_currentState.End);
			IsStarted = false;
		  if (_callBackAction != null)
		  {
		    _callBackAction();
		    _callBackAction = null;
		  }

		}

	  public void Tick(int milliseconds)
	  {
      if (_time >= _currentState.Duration)
      {
        MotionFinished();
        return;
      }

      _time += milliseconds;
	    _time = Math.Min(Duration, Math.Max(0, _time));

      Adapter.SetValue(_currentState.GetValue(_time));
	  }
	}
}
